package org.example.model;

import lombok.Data;
import org.example.exception.BizException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Data
public class UploadTask {
    private String uploadId;
    private String status;
    private boolean cancel;
    private Thread thread;
    private String uploadPath;
    private Logger logger;
    private final Lock lock=new ReentrantLock();
    public UploadTask(String uploadPath){
        this.logger= LoggerFactory.getLogger(UploadTask.class);
        this.uploadPath=uploadPath;
        this.status="initial";
        this.cancel=false;
    }
    private static int extractNumberFromFileName(Path path) {
        String fileName = path.getFileName().toString();
        String baseName = fileName.replaceFirst("[.][^.]+$", ""); // 移除扩展名
        return Integer.parseInt(baseName);
    }
    private int findFirstMissingNumber(int[] numbers,int start) {
        if (numbers == null || numbers.length == 0) {
            logger.warn("数字数组为空");
            return -1;
        }

        // 检查是否从1开始连续
        for (int i = 0; i < numbers.length; i++) {
            int expected = start+i;
            int actual = numbers[i];
            if (actual != expected) {
                return expected; // 返回第一个缺失的数字
            }
        }
        return -1; // 完全连续
    }
    private LinkedHashMap<Integer, Path> sortFilesByNumber(String uploadPath) throws IOException {
        try(
                Stream<Path> pathStream= Files.list(Paths.get(uploadPath))
        ){
            Map<Integer, Path> originalMap = pathStream
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toMap(
                            UploadTask::extractNumberFromFileName,
                            Function.identity(),
                            (existing, replacement) -> replacement
                    ));
            return originalMap.entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .collect(Collectors.toMap(
                            Map.Entry::getKey,
                            Map.Entry::getValue,
                            (oldValue, newValue) -> newValue,
                            LinkedHashMap::new
                    ));
        }
    }
    private void mergeTask(LinkedHashMap<Integer,Path> chunkFiles,Path mergedFilePath){
        for (Map.Entry<Integer, Path> entry : chunkFiles.entrySet()) {
            if(cancel){
                logger.debug("任务{}被取消",uploadId);
                return;
            }
            Path fragmentPath = entry.getValue();

            try {
                // 读取碎片文件内容
                byte[] fragmentData = Files.readAllBytes(fragmentPath);

                // 追加到目标文件
                Files.write(mergedFilePath,
                        fragmentData,
                        StandardOpenOption.CREATE,
                        StandardOpenOption.APPEND);

                // 可选：删除已合并的碎片文件
//                Files.delete(fragmentPath);
            }catch (Exception e){
                logger.error("合并文件失败：{}到{}，原因：{}",entry.getValue().toAbsolutePath(),mergedFilePath.toAbsolutePath(),e.getMessage());
                this.status="failed";
                throw new BizException("合并文件失败",null);
            }
        }
        this.status="finished";
    }
    public void startMerge(){
        if(lock.tryLock()) {
            if (status == null || !status.equals("uploading")) {
                throw new BizException("任务状态不正确", null);
            }
            LinkedHashMap<Integer, Path> sortedPaths = null;
            try {
                sortedPaths = sortFilesByNumber(uploadPath);
            } catch (IOException e) {
                logger.error("排序文件失败，原因：{}", e.getMessage());
                throw new RuntimeException(e);
            }
            int[] fileKeys = new int[sortedPaths.keySet().size()];
            int index = 0;
            for (Integer key : sortedPaths.keySet()) {
                fileKeys[index] = key;
                index++;
            }
            int first = findFirstMissingNumber(fileKeys, 0);
            if (first != -1) {
                logger.error("上传文件缺失：{}", first);
                throw new BizException("上传文件缺失：" + first, null);
            }
            Path mergedFilePath = Paths.get(uploadPath, uploadId);
            LinkedHashMap<Integer, Path> finalSortedPaths = sortedPaths;
            thread = new Thread(() -> {
                try {
                    mergeTask(finalSortedPaths, mergedFilePath);
                }finally {
                    lock.unlock();
                }
            });
            this.status = "merging";
            thread.start();
        }else{
            throw new BizException("加锁失败，有上传中的任务",null);
        }
    }
    private void clearResource(){
        lock.lock();
        try {
            Files.delete(Paths.get(uploadPath));
        } catch (Exception e) {
            logger.warn("删除上传任务资源失败：{}", e.getMessage());
        }finally {
            lock.unlock();
        }
    }
    public void cancel(){
        this.cancel=true;
        clearResource();
    }
    public void uploadChunk(int chunkIndex,InputStream inputStream){
        if(lock.tryLock()){
            try {
                Path targetPath=Paths.get(uploadPath,String.valueOf(chunkIndex));
                Files.copy(inputStream,targetPath);
            }catch (Exception e){
                logger.error("任务{}上传分片{}失败：{}",uploadId,chunkIndex,e.getMessage());
                throw new BizException("上传分片失败",null);
            }finally {
                lock.unlock();
            }
        }else{
            throw new BizException("存在并发上传",null);
        }
    }
}
